﻿/*
Configuration Server
 
Provides a single configuration point for the several instances (development, 
staging, testing, production, etc.) for web sites
 
Includes ideas and code from all over the internet. Specially, stacktrace, msdn, github.
 
Copyright (C) 2014 by Jaime Espinosa <jaimeespinosa60@gmail.com>
 
The MIT License (MIT)

Copyright (c) 2014 jaime espinosa

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

  @author Jaime Espinosa <jaimeespinosa60@gmail.com>
  @package configuracion
  @version 0.1
*/

using Configuracion.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Data.Entity;

namespace Configuracion.Controllers
{
    public class consumeController : baseController
    {
        class data
        {
            public int cacheDuration { get; set; }
            public string value { get; set; }
            public mdlBundleSet set { get; set; }
            public mdlBundleKey key { get; set; }
        }
        public async Task<JsonResult> keyForBundle(string strBundle = "", string strSet = "default", string strKey = "", string strUser = "", string strPassword = "")
        {
            tmTrack.timeTrack("keyForBundle - start");

            if ((strBundle == "") || (strKey == "") || (strUser == "") || (strPassword == ""))
            {
                return Json(new { error = "invalid parameters" }, JsonRequestBehavior.AllowGet);
            }

            if (strSet == "") strSet = "default";

            try
            {
                string strHashedPassword = HashString(strPassword);
                tmTrack.timeTrack("keyForBundle - HashString");

                if (System.Web.HttpRuntime.Cache[strBundle + strUser + strHashedPassword + strKey] == null)
                {
                    // OBTAINS THE BUNDLE SET
                    data data = await (from p in dbContext.Bundles
                          .Where(u => u.name.ToLower() == strBundle.ToLower())
                          .Where(v => v.Sets.Any(b => b.consumerUserName == strUser
                                                   && b.consumerPassword == strHashedPassword))
                          .SelectMany(p => p.KeysInSets)
                                       where p.Key.name.ToLower() == strKey && p.Set.name.ToLower() == strSet
                                       select new data
                                       {
                                           value = p.value,
                                           cacheDuration = p.Set.cacheDuration,
                                           set = p.Set,
                                           key = p.Key
                                       }).FirstOrDefaultAsync<data>();

                    tmTrack.timeTrack("keyForBundle - obtained value for key");

                    if (data == null)
                        return Json(new { error = "Value for key in set not found" }, JsonRequestBehavior.AllowGet);
                    else
                    {
                        // registers Hit Test
                        Task.Run(()=> registerHitTest(data.key, data.set));

                        tmTrack.timeTrack("keyForBundle - registerHitTest");

                        if (data.cacheDuration > 0)
                        {
                            // ADD TO CACHE
                            System.Web.HttpRuntime.Cache.Add(strBundle + strUser + strHashedPassword + strKey,
                                data.value, null,
                                DateTime.Now.AddSeconds(data.cacheDuration),
                                System.Web.Caching.Cache.NoSlidingExpiration,
                                System.Web.Caching.CacheItemPriority.Normal, null);

                            tmTrack.timeTrack("keyForBundle - cache written");
                        }
                        else
                            return Json(new { OK = data.value }, JsonRequestBehavior.AllowGet);
                    }
                }

                tmTrack.timeTrack("keyForBundle - before return");

                return Json(new { OK = System.Web.HttpRuntime.Cache[strBundle + strUser + strHashedPassword + strKey] }, JsonRequestBehavior.AllowGet);
            }
            catch (Exception ex)
            {
                return Json(new { error = infoExcepcion(ex) }, JsonRequestBehavior.AllowGet);
            }
        }
        [Authorize]
        public ActionResult keyForBundleDirect(mdlKeyForSetInBundleTesting test)
        {
            try
            {
                if (Request.Form["testForm"] != null)
                {
                    if ((test.strBundle == "") || (test.strKey == "") || (test.strUser == "") || (test.strPassword == ""))
                    {
                        ViewBag.Message = "Invalid parameters";
                    }
                    else
                    {
                        // SIMULATES REMOTE GET
                        //http://stackoverflow.com/questions/1214607/how-can-i-get-the-root-domain-uri-in-asp-net
                        ViewBag.OK = doGet(Request.Url.GetLeftPart(UriPartial.Authority), test);
                    }
                }
            }
            catch (Exception ex)
            {
                ViewBag.NOK = infoExcepcion(ex);
            }
            return View();
        }
        [Authorize]
        public ActionResult keyForBundleLibrary(string strKey)
        {
            try
            {
                if (Request.Form["testForm"] != null)
                {
                    if (strKey == "")
                    {
                        ViewBag.Message = "Invalid parameters";
                    }
                    else
                    {
                        // uses config file
                        ViewBag.OK = configurationReader.configurationReader.setting(strKey);
                        // double
                        try
                        {
                            ViewBag.OKdouble = configurationReader.configurationReader.settingAsDouble(strKey);
                        }
                        catch (Exception ex)
                        {
                            ViewBag.OKdouble = ex.Message;
                        }

                        // bool
                        try
                        {
                            ViewBag.OKbool = configurationReader.configurationReader.settingAsBool(strKey);
                        }
                        catch (Exception ex)
                        {
                            ViewBag.OKbool = ex.Message;
                        }

                        // int
                        try
                        {
                            ViewBag.OKint = configurationReader.configurationReader.settingAsInt(strKey);
                        }
                        catch (Exception ex)
                        {
                            ViewBag.OKint = ex.Message;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ViewBag.NOK = infoExcepcion(ex);
            }
            return View();
        }

        private string doGet(string strURl, mdlKeyForSetInBundleTesting test)
        {
            HttpWebRequest request = null;
            string strGetString = strURl + "/consume/keyForBundle?strBundle={0}&strSet={1}&strKey={2}&strUser={3}&strPassword={4}";
            try
            {
                strGetString = string.Format(strGetString, test.strBundle, test.strSet, test.strKey, test.strUser, test.strPassword);

                request = (HttpWebRequest)WebRequest.Create(strGetString);

                request.Method = "GET";
            }
            catch (Exception ex)
            {
                throw new Exception("consumeController::doGet:" + ex.Message);
            }

            try
            {
                // allows self generated certificate to bu used
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);

                HttpWebResponse resp = (HttpWebResponse)request.GetResponse();

                string respStr = "";

                if (request.HaveResponse)
                {
                    if (resp.StatusCode == HttpStatusCode.OK || resp.StatusCode == HttpStatusCode.Accepted)
                    {
                        StreamReader respReader = new StreamReader(resp.GetResponseStream());
                        respStr = respReader.ReadToEnd(); // get the xml result in the string object  

                        var jsonResponse = new { error = "", OK = "" };
                        jsonResponse = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType(respStr, jsonResponse);

                        if (jsonResponse.error != null)
                            throw new Exception(jsonResponse.error);
                        else
                            return jsonResponse.OK;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("consumeController::doGet:" + ex.Message);
            }

            return "";
        }
        /// <summary>
        /// Allows the self generated ssl certificate to be used. Remove in production with "real" certificate
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="certificate"></param>
        /// <param name="chain"></param>
        /// <param name="sslPolicyErrors"></param>
        /// <returns></returns>
        private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }

        private async Task registerHitTest(mdlBundleKey key, mdlBundleSet set)
        {
            DateTime now = DateTime.Now;
            int intYear = now.Year;
            int intMonth = now.Month;
            int intDay = now.Day;
            int intHour = now.Hour;
            string strError = "";

            try
            {
                // YEAR
                mdlHitTestYear hitYear = await (from h in dbContext.hitTestYear
                                                where h.Year == intYear
                                                & h.Key.ID == key.ID
                                                & h.Set.ID == set.ID
                                                select h).FirstOrDefaultAsync<mdlHitTestYear>();
                if (hitYear == null)
                    hitYear = dbContext.hitTestYear.Add(new mdlHitTestYear(intYear, key, set));

                hitYear.YearHits++;

                // MONTH
                mdlHitTestMonth hitMonth = await (from h in dbContext.hitTestMonth
                                                  where h.Year == intYear
                                                  & h.Month == intMonth
                                                  & h.Key.ID == key.ID
                                                  & h.Set.ID == set.ID
                                                  select h).FirstOrDefaultAsync<mdlHitTestMonth>();

                if (hitMonth == null)
                    hitMonth = dbContext.hitTestMonth.Add(new mdlHitTestMonth(intYear, intMonth, key, set));

                hitMonth.MonthHits++;


                // DAY
                mdlHitTestDay hitDay = await (from h in dbContext.hitTestDay
                                              where h.Year == intYear
                                              & h.Day == intDay
                                              & h.Key.ID == key.ID
                                              & h.Set.ID == set.ID
                                              select h).FirstOrDefaultAsync<mdlHitTestDay>();

                if (hitDay == null)
                    hitDay = dbContext.hitTestDay.Add(new mdlHitTestDay(intYear, intMonth, intDay, key, set));

                hitDay.DayHits++;


                // HOUR
                mdlHitTestHour hitHour = await (from h in dbContext.hitTestHour
                                                where h.Year == intYear
                                                & h.Hour == intHour
                                                & h.Key.ID == key.ID
                                                & h.Set.ID == set.ID
                                                select h).FirstOrDefaultAsync<mdlHitTestHour>();

                if (hitHour == null)
                    hitHour = dbContext.hitTestHour.Add(new mdlHitTestHour(intYear, intMonth, intDay, intHour, key, set));

                hitHour.HourHits++;

                if (dbContext.GetValidationErrors().Count() > 0)
                {
                    foreach (System.Data.Entity.Validation.DbEntityValidationResult error in dbContext.GetValidationErrors())
                        foreach (System.Data.Entity.Validation.DbValidationError valerror in error.ValidationErrors)
                            strError += valerror.ErrorMessage.ToString() + "<br/>";
                }
                else
                {
                    await dbContext.SaveChangesAsync();
                }
            }
            catch
            {

            }
        }
    }

}